/*
*    Reversi Contender homepage:  http://code.google.com/p/reversi-contender
*
*    copyright 2011 steven sims <steven.joseph.sims at gmail.com>
*
*    This file is part of Reversi Contender.
*
*    Reversi Contender is free software: you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    Reversi Contender is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with Reversi Contender.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.threadswarm.reversi.gui;

import java.awt.BorderLayout;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.io.IOUtils;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 *
 * @author steve
 */
public class CheckVersionActionListener implements ActionListener {
    
    private final static String HTML_BASE = "<html><body id=\"body\" style=\"font: 12pt sans-serif\"></body></html>";
    
    private final JFrame frame;
    private final JDialog dialog;
    private final JButton checkButton;
    private final JProgressBar progressBar;
    private final JEditorPane editorPane;
    private URL updateUrl = null;
    private double versionNumber = -1.00;
    private volatile String downloadUrl;
    
    public CheckVersionActionListener(JFrame frame) {
        this.frame = frame;
        try {
            updateUrl = new URL("http://reversi-contender.googlecode.com/files/version-info.xml");
        } catch (MalformedURLException ex) {
            throw new RuntimeException();  //won't happen
        }
        
        dialog = new JDialog(frame, "Check for Updates", true);
        dialog.setPreferredSize(new Dimension(320, 140));
        dialog.setResizable(false);
        JPanel contentPanel = (JPanel) dialog.getContentPane();
        contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS));
        
        JPanel controlsPanel = new JPanel();
        checkButton = new JButton("Check");
        checkButton.addActionListener(new CheckVersionButtonListener(this));
        progressBar = new JProgressBar(0, 100);
        progressBar.setStringPainted(true);
        progressBar.setEnabled(false);
        controlsPanel.add(checkButton);
        controlsPanel.add(progressBar);
        contentPanel.add(controlsPanel);
        
        editorPane = new JEditorPane("text/html", HTML_BASE);
        editorPane.setEditable(false);
        editorPane.addHyperlinkListener(new HyperlinkListener(){

            @Override
            public void hyperlinkUpdate(HyperlinkEvent he) {
                if(he.getEventType() == HyperlinkEvent.EventType.ACTIVATED){
                    if(downloadUrl != null && !downloadUrl.isEmpty()){
                        Desktop desktop = Desktop.getDesktop();
                        try{
                            desktop.browse(URI.create(downloadUrl));
                         }catch(Exception ex) {
                              JOptionPane.showMessageDialog(dialog, "We can't seem to open your browser, sorry!", "Browser Error", JOptionPane.ERROR_MESSAGE);
                         }
                    }
                }
            }
        });
        JScrollPane textPanel = new JScrollPane(editorPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        textPanel.setPreferredSize(new Dimension(305, 100));
        textPanel.setAutoscrolls(true);
        contentPanel.add(textPanel);
        
        contentPanel.add(new JSeparator(SwingConstants.HORIZONTAL));
        JPanel bottomPanel = new JPanel();
        bottomPanel.setLayout(new BorderLayout());
        JButton closeButton = new JButton("Close");
        closeButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                dialog.setVisible(false);
            }
        });
        bottomPanel.add(closeButton, BorderLayout.LINE_END);
        contentPanel.add(bottomPanel);
        
        dialog.pack();
        
    }

    @Override
    public void actionPerformed(ActionEvent ae) {
        checkButton.setEnabled(true);
        progressBar.setValue(0);
        progressBar.setEnabled(false);
        editorPane.setText(HTML_BASE);
              
        dialog.setLocationRelativeTo(frame);
        dialog.setVisible(true);
    }
    
    public void setDownloadURL(String downloadUrl) {
        this.downloadUrl = downloadUrl;
    }
    
    private static void appendElementHTML(JEditorPane editorPane, String id, String content) {
        javax.swing.text.html.HTMLDocument document = (javax.swing.text.html.HTMLDocument) editorPane.getDocument();
        try {
            Element element = document.getElement(id);
            document.insertBeforeEnd(element, content);
            editorPane.setCaretPosition(document.getLength());
        } catch (BadLocationException ex) {
            throw new RuntimeException();  //won't happen
        } catch (IOException ex) {
            throw new RuntimeException();  //won't happen
        }
    }
    
    private class CheckVersionButtonListener implements ActionListener {
        
        private final CheckVersionActionListener cval;
        
        public CheckVersionButtonListener(CheckVersionActionListener cval) {
            this.cval = cval;
        }

        @Override
        public void actionPerformed(ActionEvent ae) {
            editorPane.setText(HTML_BASE); 
            checkButton.setEnabled(false);
            progressBar.setValue(0);
            progressBar.setEnabled(true);
            Thread t = new Thread(new CheckVersionWorker(cval));
            t.start();
        }
        
    }
    
    private class CheckVersionWorker implements Runnable {
        
        private double latestVersion;
        private CheckVersionActionListener cval;
        
        public CheckVersionWorker(CheckVersionActionListener cval) {
            this.cval = cval;
        }

        @Override
        public void run() {
            HttpURLConnection connection = null;
            InputStream instream = null;
            try{
                sendMessage("Connecting to server..");
                updateProgress(17);
                connection = (HttpURLConnection) updateUrl.openConnection();
                connection.setConnectTimeout(4000);
                sendMessage("Connected<br/>Downloading version info..<br/>");
                instream = connection.getInputStream();
                updateProgress(50);
                sendMessage("Parsing XML..<br/>");
                updateProgress(75);
                parseInfoFromInputStream(instream);
                sendMessage("Comparing version information..<br/>");
                updateProgress(95);
                String message;
                if(versionNumber < latestVersion){
                    message = "<a id=\"url\" href=\"" + downloadUrl + "\">A NEW version is available</a><br/>";
                }else{
                    message = "You have the newest version!<br/>";
                }
                sendMessage(message);
                updateProgress(100);
            }catch(IOException ex){
                sendMessage("ERROR -- unable to connect to server<br/>");
            }finally{
                IOUtils.closeQuietly(instream);
                if(connection != null)
                    connection.disconnect();
                done();
            }
        }
        
        public void sendMessage(final String message) {
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    CheckVersionActionListener.appendElementHTML(editorPane, "body", message);
                }
            });
        }
        
        public void updateProgress(final int progress) {
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    progressBar.setValue(progress);
                }
            });
        }
        
        private void done() {
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    checkButton.setEnabled(true);
                }
            });
        }
        
        private void parseInfoFromInputStream(InputStream instream) throws IOException {
            try {
                DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
                Document document = builder.parse(instream);
                NodeList versionElementNodes = document.getElementsByTagName("version");
                if(versionElementNodes.getLength() > 0){
                    String versionString = versionElementNodes.item(0).getTextContent();
                    if(versionString != null && !versionString.isEmpty()){
                        latestVersion = Double.parseDouble(versionString);
                    }
                }
                NodeList urlElementNodes = document.getElementsByTagName("url");
                if(urlElementNodes.getLength() > 0){
                    String url = urlElementNodes.item(0).getTextContent();
                    cval.setDownloadURL(url);
                }
            } catch (ParserConfigurationException ex) {
                throw new RuntimeException();  //won't happen
            } catch (SAXException ex) {
                //TODO
            }
   
        }
        
    }
    
    
}
